home *** CD-ROM | disk | FTP | other *** search
Text File | 1993-09-16 | 37.6 KB | 946 lines | [TEXT/CCL2] |
- ;;;-*-Mode: LISP; Package: CCL -*-
-
- ;;;;;;;;;;;;;;;;;;;;;;;;;
- ;;;
- ;;; Binhex.lisp is an example of creation of a standalone application with no
- ;;; Lisp listener in evidence.
- ;;; The first section contains the code for encoding and decoding
- ;;; files in binhex format.
- ;;; The second section contains the menu and dialogs for the user interface.
- ;;; and installs the binhex menu in the current environment.
- ;;; We have not attempted to create carefully worded beautiful dialogs.
- ;;; The last section contains the functions for making a standalone
- ;;; binhex application. Do (ccl::SAVE-BINHEX pathname) to make the application.
-
- ;;;;;;;;;;;;;;;;;;
- ;;; Modification history
- ;; 04/28/93 mwp Release
- ;; 06/22/92 alice fix for finder selected files when already running (requires appleevents-patch)
- ;; and change bit-bucket stream to an empty broadcast stream
- ;;---------- 2.0
- ;; 01/15/91 alice some folks do rle in header too.
- ;; 12/10/91 alice handle file errors
-
- (in-package :ccl)
-
- (eval-when (:compile-toplevel :execute :load-toplevel)
- (require :lispequ)
- (require :resources))
-
- ; to do - an icon and bundle bit
-
- ; magic number for the crc calculation
- (defconstant magic #.(ash #x1021 8))
-
- ; encoding translation table
- (defconstant char-table
- "!\"#$%&'()*+,-012345689@ABCDEFGHIJKLMNPQRSTUVXYZ[`abcdefhijklmpqr")
-
-
- ; the file creator for encoded binhex files
- (defconstant binhex-file-creator :|BnHQ|)
-
- ; this value denotes a white space character in the decoding translation table
- (defconstant return-code #xc0)
-
- (defconstant colon-code (char-code #\:))
-
- (defparameter decode-table nil)
-
- (defparameter crc-table (make-array 256))
-
- ;;; Create the table used by the crc calculation.
- (defun make-crc ()
- (dotimes (i 256)
- (setf (svref crc-table i)
- (let ((mgc (ash magic -1))
- (val (ash i 16)))
- (do ((bit 23 (1- bit)))
- ((<= bit 15))
- (when (logbitp bit val)
- (setq val (logxor val mgc)))
- (setq mgc (ash mgc -1)))
- (logand val #xffff)))))
-
- ; xor f(crc high byte) with crc low byte and new byte
-
- (defmacro crc-byte (crc byte)
- (let ((cc (gensym))(bb (gensym)))
- `(let ((,cc ,crc)(,bb ,byte))
- (declare (type (unsigned-byte 16) ,cc)(type (unsigned-byte 8) ,bb)
- (type (simple-vector fixnum 256) crc-table))
- (logand #xffff (logxor (the fixnum (svref crc-table (the (unsigned-byte 8)(ash ,cc -8))))
- (logior (ash ,cc 8) ,bb))))))
- #|
- (defun crc-byte (crc byte)
- (declare (type (unsigned-byte 16) crc)(type (unsigned-byte 8) byte)
- (type (simple-array fixnum 256) crc-table))
- (logand #xffff (logxor (the fixnum (svref crc-table (the (unsigned-byte 8)(ash crc -8))))
- (logior (ash crc 8) byte))))
- |#
-
- ;;; above is equivalent to
- #|
- (defun crc-byte (crc byte)
- (let ((foo (logior (ash crc 8) byte)) (mgc magic))
- (dotimes (i 8)
- (setq foo (ash foo 1))
- (when (logbitp 24 foo)
- (setq foo (logxor foo mgc))))
- (logand #xffff (ash foo -8))))
- |#
-
-
- ; set up decoding translation table
- ; #xFF denotes a character that should not appear in the binhex stream
- (eval-when (:execute :load-toplevel)
- (setq decode-table
- (make-array 256 #|:element-type '(unsigned-byte 8)|# :initial-element #xFF))
- (dotimes (i (length char-table))
- (let ((code (char-code (schar char-table i))))
- (setf (aref decode-table code) i)))
- (dolist (c '(#\newline #\return #\linefeed #\tab #\space))
- (setf (aref decode-table (char-code c)) return-code))
- (make-crc))
-
-
-
- ; the full header to print at the front of a binhex file
- (defconstant full-header
- "(This file must be converted with BinHex 4.0)
- :")
-
- ; that part of the header to check when decoding a binhex file
- (defconstant short-header
- "This file must be converted with BinHex")
-
- ;;; Define a new stream class and a few methods.
- ;;; Avoids making a string when decoding a selection in a Fred window.
- (defclass fred-input-stream (input-stream)
- ((my-buffer :initarg :buffer)
- (index :initarg :start :initform nil)
- (pathname :initform "a Fred selection" :reader stream-filename)
- (end :initarg :end :initform nil)))
-
- (defmethod instance-initialize :after ((stream fred-input-stream) &key)
- (let* ((buffer (slot-value stream 'my-buffer))
- (index (or (slot-value stream 'index) 0))
- (length (buffer-size buffer))
- (end (or (slot-value stream 'end) length)))
- (unless (<= 0 end length) (error "End ~S not between 0 and length ~S" end length))
- (unless (<= 0 index end) (error "Index ~S not between 0 and end ~S" index end))
- (setf (slot-value stream 'index) index)
- (setf (slot-value stream 'end) end)))
-
- (defmethod stream-read-byte ((stream fred-input-stream))
- (let ((idx (slot-value stream 'index)))
- (declare (fixnum idx))
- (when (< idx (the fixnum (slot-value stream 'end)))
- (setf (slot-value stream 'index) (the fixnum (+ idx 1)))
- (char-code (buffer-char (slot-value stream 'my-buffer) idx)))))
-
- (defmethod stream-reader ((stream fred-input-stream))
- (values (method-function (method stream-read-byte (fred-input-stream)))
- stream))
-
- (defmethod file-length ((stream fred-input-stream) &optional ignore)
- (declare (ignore ignore))
- (- (slot-value stream 'end)(slot-value stream 'index)))
-
- (defmethod stream-position ((stream fred-input-stream) &optional position)
- (if position
- (setf (slot-value stream 'index) position)
- (slot-value stream 'index)))
-
- ; do I need this?
- (defmethod stream-eofp ((stream fred-input-stream))
- (eq (slot-value stream 'index) (slot-value stream 'end)))
-
- (defmethod stream-close :after ((stream fred-input-stream))
- (slot-makunbound stream 'my-buffer))
-
- (defclass binhex-application (application)
- ())
-
-
- (defun binhex-decode (infile &optional outfile)
- (with-open-file (s infile :direction :input :element-type '(unsigned-byte 8))
- (binhex-decode-stream s outfile infile)))
-
- ; bx-byte reads a byte from the binhex file - gets the 6 bit translation
- ; combines those bits with some left over from the last 6 bit translation
- ; and returns 8 bits for output. Note that we cannot do the
- ; CRC here because the byte(s) actually output may be different.
-
-
- (defun bx-byte (reader readarg)
- (declare (special bits-left count last-nibble last-byte istream))
- (declare (type (unsigned-byte 8) bits-left last-nibble))
- (declare (fixnum count))
- (declare (optimize (speed 3)(safety 0)))
- (flet
- ((bx-error ()
- (error (make-condition 'file-error
- :pathname (let ((fn (stream-filename istream)))
- (or (probe-file fn) fn))
- :error-type "End of file ~S"
- :format-arguments nil))))
- (macrolet
- ((read-byte-reader ()
- `(let ((c (funcall reader readarg)))
- (cond
- (c (locally (declare (type (unsigned-byte 8) c))
- ;(when (eq c colon-code)(binhex-error "premature colon in ~A" istream))
- (setq c (svref table c))
- (when (eq c #xFF) (binhex-error "~A contains an illegal character" istream))
- (loop (when (neq c return-code)(return))
- (setq c (svref table (funcall reader readarg))))
- c))
- (t (bx-error)))))
- (bx-byte-sub ()
- `(let ((c1 (read-byte-reader)))
- (declare (type (unsigned-byte 8) c1))
- (case bits-left
- (0
- (setq last-nibble (read-byte-reader))
- (setq bits-left 4)
- (logior (ash c1 2)(ash last-nibble -4)))
- (4
- (setq bits-left 2)
- (logior (logand #xf0 (ash last-nibble 4))
- (ash (setq last-nibble c1) -2)))
- (t (setq bits-left 0)
- (logand #xff (logior (ash last-nibble 6) c1)))))))
- (let ((table decode-table))
- (declare (type (simple-array fixnum 256) table))
- (cond ((> count 0)
- (setq count (1- count)))
- (t (let ((byte (bx-byte-sub)))
- (cond
- ((and (eq byte #x90)(neq 0 (setq count (bx-byte-sub))))
- (setq count (- count 2)))
- (t (setq last-byte byte))))))
- last-byte))))
-
- (defun binhex-decode-stream (istream &optional outfile (infile istream))
- (declare (special istream))
- (declare (optimize (speed 3)(safety 0)))
- (let ((bits-left 0)(last-nibble 0)(count 0) last-byte)
- (declare (special bits-left count last-nibble last-byte))
- (declare (type (unsigned-byte 8) bits-left last-nibble))
- (declare (fixnum count))
- (multiple-value-bind (reader readarg)(stream-reader istream)
- (macrolet
- ((bx-long ()
- `(let ((c1 (bx-byte reader readarg))(c2 (bx-byte reader readarg))
- (c3 (bx-byte reader readarg))(c4 (bx-byte reader readarg)))
- (setq crc (crc-byte (crc-byte (crc-byte (crc-byte crc c1) c2) c3) c4))
- (logior
- (ash c1 24)
- (ash c2 16)
- (ash c3 8)
- c4))))
- (let ((c 0))
- (declare (fixnum c))
- (when (not (find-binhex-header istream))
- (binhex-error "~A does not have a binhex header" infile))
- ; skip to return
- (loop
- (setq c (read-byte istream))
- (unless (eq c (char-code #\space))
- (when (eq (aref decode-table c) return-code)
- (return))))
- ; skip returns til colon
- (loop
- (setq c (read-byte istream))
- (when (eq c colon-code) (return))
- (when (neq (aref decode-table c) return-code)
- (binhex-error "Bad stuff in text header of ~A" infile)))
- ; time to read the header describing the contents
- (let* ((namelength (bx-byte reader readarg))
- (name (make-string namelength))
- (type (make-string 4))
- (creator (make-string 4))
- (crc 0)
- flags dlen rlen hdr-crc)
- ; get the filename - will be the default for the dialog
- (setq crc (crc-byte 0 namelength))
- (dotimes (i namelength)
- (declare (fixnum i))
- (let ((c (bx-byte reader readarg)))
- (setq crc (crc-byte crc c))
- (setf (aref name i)(code-char c))))
- ; skip a 0 byte
- (when (neq 0 (bx-byte reader readarg))(binhex-error "Error reading file name in header of ~A" infile))
- (setq crc (crc-byte crc 0))
- (when (null outfile)
- (setq outfile
- (catch-cancel (choose-new-file-dialog :directory name)))
- (when (eq outfile :cancel)(return-from binhex-decode-stream nil)))
- (with-cursor *watch-cursor* ; have to do this after the modal dialog
- ; get mac type and creator
- (dotimes (i 4)
- (declare (fixnum i))
- (let ((c (bx-byte reader readarg)))
- (setq crc (crc-byte crc c))
- (setf (aref type i) (code-char c))))
- (setq type (intern type (find-package :keyword)))
- (dotimes (i 4)
- (declare (fixnum i))
- (let ((c (bx-byte reader readarg)))
- (setq crc (crc-byte crc c))
- (setf (aref creator i)(code-char c))))
- (setq creator (intern creator (find-package :keyword)))
- ; finder flags
- (let ((c (bx-byte reader readarg)) (c2 (bx-byte reader readarg)))
- (setq crc (crc-byte (crc-byte crc c) c2))
- (setq flags (logior (ash c 8) c2)))
- ; lengths of data and resource forks
- (setq dlen (bx-long))
- (setq rlen (bx-long))
- (setq crc (crc-byte (crc-byte crc 0) 0))
- (setq hdr-crc (logior (ash (bx-byte reader readarg) 8)(bx-byte reader readarg)))
- (when (neq crc hdr-crc) (binhex-error "crc failure in header of ~A" infile))
- (binhex-decode-sub outfile reader readarg type creator dlen :data)
- (binhex-decode-sub outfile reader readarg type creator rlen :resource)
- (set-finder-flags outfile
- (logand flags
- (lognot (+ (ash 1 8) ;#$fInitted - where is he
- #$fOnDesk
- #$fInvisible))))
- outfile)))))))
-
- (defun set-finder-flags (file flags)
- (%stack-iopb (pb np)
- (%path-to-iopb file pb :errchk)
- (setf (pref pb hparamblockrec.ioFlFndrInfo.fdFlags) flags)
- (file-errchk (#_HSetFInfo pb) file)))
-
- (defun get-finder-flags (file)
- (%stack-iopb (pb np)
- (%path-to-iopb file pb :errchk)
- (pref pb hparamblockrec.ioFlFndrInfo.fdFlags)))
-
-
-
- (defun find-binhex-header (s)
- (let ((hlength (length short-header))
- (flength (file-length s))
- (pos))
- (declare (fixnum hlength flength))
- (declare (optimize (speed 3)(safety 0)))
- (dotimes (i (- flength hlength) nil)
- (declare (fixnum i))
- (let ((c (code-char (read-byte s))))
- (when (eq c (schar short-header 0))
- (setq pos (stream-position s))
- (when (dotimes (i (1- hlength) t)
- (declare (fixnum i))
- (when (neq (schar short-header (1+ i)) (code-char (read-byte s)))
- (return nil)))
- (return-from find-binhex-header t))
- (stream-position s pos))))))
-
- ; decode the resource or data fork section of the binhex data file
-
- (defun binhex-decode-sub (outfile reader readarg type creator dlen fork)
- (declare (optimize (speed 3)(safety 0)))
- (declare (special istream))
- (with-open-file (ostream outfile :direction :output
- :if-exists (if (eq fork :data) :supersede :overwrite)
- :external-format type
- :mac-file-creator creator
- :fork fork
- :element-type '(unsigned-byte 8))
- (multiple-value-bind (writer writearg)(stream-writer ostream)
- (let ((crc 0))
- (do ((i dlen (1- i)))
- ((<= i 0))
- ; does the length include the crc? assume not
- (let ((byte (bx-byte reader readarg)))
- (funcall writer writearg byte)
- (setq crc (crc-byte crc byte))))
- ; account for 2 crc bytes as if zero
- (setq crc (crc-byte crc 0))(setq crc (crc-byte crc 0))
- (when (not (and (eq (logand #xFF (ash crc -8))(bx-byte reader readarg))
- (eq (logand #xff crc)(bx-byte reader readarg))))
- (binhex-error "crc failure in ~A" istream))))))
-
-
- ; bx-out
- ; given 8 bits to output, combines some of the high bits with some of the low
- ; bits of the last byte, to get a 6 bit byte which is translated and output
- ; in one case 2 translated 6 bit bytes are output
- ; Also inserts a #\newline every 64 characters
-
- (defun bx-out (writer writearg byte &aux (table char-table))
- (declare (special last-byte bits-left nchars))
- (declare (fixnum nchars)(type (unsigned-byte 8) byte last-byte))
- (declare (optimize (speed 3)(safety 0)))
- (case bits-left
- (0 (funcall writer writearg (schar table (ash byte -2)))
- (setq last-byte byte)
- (setq bits-left 2))
- (2 (funcall writer writearg (schar table (logand #o77 (logior
- (ash last-byte 4)
- (ash byte -4)))))
- (setq last-byte byte)
- (setq bits-left 4))
- (t (funcall writer writearg (schar table (logand #o77 (logior (ash last-byte 2)
- (ash byte -6)))))
- (setq nchars (1+ nchars))
- (when (> nchars 63)
- (funcall writer writearg #\newline)
- (setq nchars 0))
- (funcall writer writearg (schar table (logand byte #o77)))
- (setq bits-left 0)))
- (setq nchars (1+ nchars))
- (when (> nchars 63)
- (funcall writer writearg #\newline)
- (setq nchars 0))
- byte)
-
- (defun binhex-encode (infile outfile &aux (crc 0) dlen rlen)
- (declare (optimize (speed 3)(safety 0)))
- (let ((bits-left 0)
- (nchars 1) ; 1 for the initial ":" on the first line
- (last-byte 0)
- writer writearg)
- (declare (special nchars bits-left last-byte))
- (declare (type (unsigned-byte 8) bits-left last-byte))
- (declare (fixnum nchars))
- (macrolet
- ((bx-out-long (n)
- (let ((sym (gensym)))
- `(let* ((,sym ,n) (byte (logand #xff (ash ,sym -24))))
- (bx-out writer writearg byte)
- (setq crc (crc-byte crc byte))
- (bx-out writer writearg (setq byte (logand #xff (ash ,sym -16))))
- (setq crc (crc-byte crc byte))
- (bx-out writer writearg (setq byte (logand #xff (ash ,sym -8))))
- (setq crc (crc-byte crc byte))
- (bx-out writer writearg (setq byte (logand #xff ,sym)))
- (setq crc (crc-byte crc byte)))))
- (bx-out-string (string)
- (let ((sym (gensym)))
- `(let ((,sym ,string))
- (dotimes (i (length ,sym))
- (declare (fixnum i))
- (let ((c (char-code (schar ,sym i))))
- (bx-out writer writearg c)
- (setq crc (crc-byte crc c))))))))
- (catch-cancel
- (with-open-file (istream infile :direction :input :element-type '(unsigned-byte 8))
- (setq infile (pathname istream))
- (setq dlen (file-length istream)))
- (with-open-file (istream infile :direction :input :element-type '(unsigned-byte 8)
- :fork :resource)
- (setq rlen (file-length istream)))
- (with-cursor *watch-cursor*
- (with-open-file (ostream outfile :direction :output :if-exists :supersede
- :mac-file-creator binhex-file-creator)
- (multiple-value-setq (writer writearg)(stream-writer ostream))
- (setq outfile (pathname ostream))
- (stream-write-entire-string ostream full-header)
- (let* ((name (file-namestring infile))
- (length (length name)))
- ; now we encode and compute crc for the header
- (bx-out writer writearg length)
- (setq crc (crc-byte crc length))
- (bx-out-string name)
- (bx-out writer writearg 0)
- (setq crc (crc-byte crc 0))
- (bx-out-string (symbol-name (mac-file-type infile)))
- (bx-out-string (symbol-name (mac-file-creator infile)))
- (let ((flags (get-finder-flags infile)) byte) ; get the finder flags
- (bx-out writer writearg (setq byte (ash flags -8)))
- (setq crc (crc-byte crc byte))
- (bx-out writer writearg (setq byte (logand #xFF flags)))
- (setq crc (crc-byte crc byte)))
- (bx-out-long dlen)
- (bx-out-long rlen)
- (setq crc (crc-byte (crc-byte crc 0) 0))
- (bx-out writer writearg (ash crc -8))
- (bx-out writer writearg (logand #xff crc))
- ; at last we get to do the real work
- (binhex-encode-sub infile dlen :data writer writearg)
- (binhex-encode-sub infile rlen :resource writer writearg)
- (bx-out writer writearg 0) ; pump out the last bits - may cause extra ! which is ok.
- (stream-tyo ostream #\:)))) ; now we must be done
- outfile))))
-
- ; Encode a fork with run length encoding. I belive stuffit does not do this or if
- ; it does, it has a different threshold.
-
- (defun binhex-encode-sub (infile file-length fork writer writearg)
- (declare (optimize (speed 3)(safety 0)))
- (with-open-file (istream infile :direction :input :element-type '(unsigned-byte 8) :fork fork)
- (multiple-value-bind (reader readarg)(stream-reader istream)
- (flet ((ferror ()
- (error (make-condition 'file-error
- :pathname (stream-filename istream)
- :error-type "End of file ~S"
- :format-arguments nil))))
- (let ((crc 0) byte last-byte)
- (declare (fixnum crc))
- (do ((i file-length (1- i)))
- ((<= i 0))
- ;(declare (fixnum i))
- (setq byte (or (funcall reader readarg) (ferror)))
- (setq crc (crc-byte crc byte))
- (when (eq byte last-byte)
- (let ((count 2))
- (declare (fixnum count))
- (loop
- (when (<= i 1)(setq byte nil) (return))
- (setq byte (or (funcall reader readarg)(ferror)))
- (setq i (1- i))
- (setq crc (crc-byte crc byte))
- (when (neq byte last-byte)(return))
- (setq count (1+ count)))
- (while (> count 255)
- (bx-out writer writearg #x90)
- (bx-out writer writearg 255)
- (setq count (- count 255))
- (bx-out writer writearg last-byte)
- (when (eq last-byte #x90)(bx-out writer writearg 0)))
- (cond ((or (> count 3)(and (eq last-byte #x90)(> count 1)))
- (bx-out writer writearg #x90)
- (bx-out writer writearg count))
- ((< count 2))
- (t (when (eq count 3) (bx-out writer writearg last-byte))
- (bx-out writer writearg last-byte)))))
- (when byte
- (bx-out writer writearg byte)
- (when (eq byte #x90) (bx-out writer writearg 0)))
- (setq last-byte byte))
- (setq crc (crc-byte (crc-byte crc 0) 0))
- (bx-out writer writearg (ash crc -8))
- (bx-out writer writearg (logand crc #xff)))))))
-
-
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
- ;;;
- ;;; Binhex front end
-
- (defvar *binhex-menu* (make-instance 'menu :menu-title "Binhex"
- :help-spec "Use this menu to encode and decode files in Binhex format."))
-
-
-
- (defun binhex-error (string &rest args)
- (declare (dynamic-extent args))
- (let ((car (car args)))
- (when (streamp car)
- (setq args (cons
- (cond ((typep car 'file-stream)(pathname car))
- (t "the selection"))
- (cdr args)))))
- (ok-cancel-dialog (apply 'format nil string args)))
-
- ;;; The dialog used to complain about suspicious file contents.
-
-
- ;;; Create and install the binhex menu
- (defun binhex-setup ()
- (let ((menu *binhex-menu*))
- (add-new-item menu "Binhex Encode…"
- 'binhex-encode-get-file
- :help-spec "Select this to encode a file in Binhex 4.0 format")
- (add-new-item menu "Binhex Decode…"
- 'binhex-decode-get-file
- :Help-spec "Select this to decode a Binhex 4.0 file")
- (add-new-item menu "Decode Selection…"
- 'binhex-decode-fred
- :class 'window-menu-item
- :update-function 'decode-selection-update
- :help-spec "Select this to decode a selection in a Fred window")
- (menu-install menu)))
-
- ;;; Enable the menu item if there is a selection, otherwise disable it.
- (defun decode-selection-update (item)
- (let ((w (front-window)))
- (when w
- (multiple-value-bind (b e)(selection-range w)
- (cond
- ((and b e (neq b e) (> (- e b) (length full-header)))
- (menu-item-enable item))
- (t (menu-item-disable item)))))))
-
- ;;;Decode a selection in a fred-window
- (defun binhex-decode-fred (w)
- (multiple-value-bind (b e)(selection-range w)
- (when (and b e (neq b e))
- (let ((stream (make-instance 'fred-input-stream
- :buffer (fred-buffer w)
- :start b
- :end e)))
- (binhex-decode-stream stream)))))
-
- (defun binhex-encode-get-file ()
- (let ((infile (catch-cancel (choose-file-dialog :button-string "Encode"))))
- (unless (eq infile :cancel)
- (binhex-encode-get-outfile infile))))
-
- (defun binhex-encode-get-outfile (infile)
- (let ((outfile
- (catch-cancel
- (choose-new-file-dialog
- :directory (make-pathname :directory (directory-namestring infile)
- :name (file-namestring infile)
- :type "hqx"
- :defaults NIL)))))
- (unless (eq outfile :cancel)
- (binhex-encode infile outfile))))
-
- (defun binhex-decode-get-file ()
- (let ((infile (catch-cancel (choose-file-dialog :button-string "Decode" :mac-file-type :TEXT))))
- (unless (eq infile :cancel)
- (binhex-decode infile))))
-
- ;;;;;;;;;;;;;;;;
- ;;
- ;; below extracted from examples;icon-dialog-item.lisp
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
- ;;
- ;; plot-icon
- ;;
- ;; a function for displaying icons. It can be passed a pointer or a number
- ;; if passed a pointer, it assumes this is a pointer to an icon record.
- ;; if passed a number, it assumes this is the resource id of an icon.
- ;; Draws to the current grafport, so call it inside WITH-FOCUSED-VIEW.
-
- (defun plot-icon (icon point size &optional color-p)
- "draws icon at point with given size"
- (unless (or (typep icon 'fixnum)
- (pointerp icon))
- (error "~s is not a valid icon (not a resource-id or pointer"))
- (with-macptrs ((resource (%null-ptr))) ; don't cons macptr's
- (without-interrupts
- (when (typep icon 'fixnum)
- (if color-p
- (%setf-macptr resource (#_getCicon icon))
- (%setf-macptr resource (#_geticon icon)))
- (when (%null-ptr-p resource)
- (error "no icon resource with id ~s ." icon))
- (setq icon resource))
- (rlet ((r :rect ;allocate a rectangle
- :topleft point
- :bottomright (add-points point size)))
- (if color-p
- (#_plotCicon r icon)
- (#_ploticon r icon))))))
-
- (defconstant *warn-icon* 2)
-
- (defclass icon-dialog-item (dialog-item)
- ((icon :initform *warn-icon* :initarg :my-icon :initarg :icon :accessor icon)
- (color-p :initform nil :initarg :color-p :accessor color-p)))
-
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
- ;;
- ;; view-default-size
- ;;
-
- (defmethod view-default-size ((view icon-dialog-item))
- #@(32 32))
-
-
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
- ;;
- ;; set-view-size
- ;; The default method does not invalidate the old rectangle
- ;;
-
- (defmethod set-view-size :before ((view icon-dialog-item) h &optional v)
- (declare (ignore h v))
- (invalidate-view view))
-
-
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
- ;;
- ;; view-draw-contents
- ;;
- ;;this is the function called by the system whenever it needs to draw the item
- ;;
- ;;
-
- (defmethod view-draw-contents ((item icon-dialog-item))
- (plot-icon (icon item) (view-position item) (view-size item) (color-p item)))
-
- (defun ok-cancel-dialog (message &key (size #@(318 145))
- (position (list :top (+ 2 *menubar-bottom*)))
- (ok-text "Ok")
- (cancel-text "Cancel"))
- (modal-dialog
- (make-instance 'keystroke-action-dialog ; allows typing first char of button text
- :window-type :double-edge-box
- :view-size size
- :view-position position
- :window-show nil
- :help-spec "This dialog appears when Binhex suspects that a file may be corrupt"
- :view-subviews
- `(
- ,(make-dialog-item 'static-text-dialog-item
- #@(20 50) (subtract-points size #@(30 50))
- message nil :help-spec "The file Binhex is encoding or decoding")
- ,(make-dialog-item 'icon-dialog-item
- #@(20 12)
- #@(32 32)
- "Untitled"
- Nil
- :icon *warn-icon*)
- ,@(if ok-text
- `(,(make-dialog-item (if cancel-text 'button-dialog-item 'default-button-dialog-item)
- (make-point (subtract-points size (if cancel-text #@(200 27) #@(102 27))))
- #@(74 18) ok-text
- #'(lambda (item)
- (declare (ignore item))
- (return-from-modal-dialog nil))
- :help-spec "Choose this if you feel lucky")))
- ,@(if cancel-text
- `(,(make-dialog-item 'default-button-dialog-item
- (subtract-points size #@(102 27))
- #@(74 18) cancel-text
- #'(lambda (item)
- (declare (ignore item))
- (return-from-modal-dialog :cancel))
- :help-spec "Choose this to cease decoding or encoding the file")))))))
-
-
-
- (unless (find-menu "Binhex") (binhex-setup))
-
- ;;;;;;;;;;;;;;;
- ;;; standalone application stuff
- ;;;
-
-
- ;;; define a bit-bucket stream and a few methods
- ;;; Somewhere I saw the idea of making a bit-bucket stream as an empty broadcast stream
-
- #|
- (defclass bit-bucket (output-stream) ())
-
- (defmethod stream-tyo ((s bit-bucket) char)
- (declare (ignore s char)))
-
- (defmethod stream-write-string ((s bit-bucket) string start end)
- (declare (ignore s string start end)))
-
- (defmethod stream-fresh-line ((s bit-bucket))
- (declare (ignore s)))
-
- (defmethod stream-force-output ((s bit-bucket))
- (declare (ignore s)))
- |#
-
- (defparameter *bit-bucket* (make-instance 'broadcast-stream :streams nil))
-
- (defparameter *debugging* nil "Set to 0 for break-loop on error, 1 for backtrace to a file")
-
- ; the condition handler for serious-error (the superclass of error)
- (defun binhex-unexpected-error (c)
- (case *debugging*
- (0
- (setq *terminal-io* (make-instance 'terminal-io)
- *error-output* *terminal-io*
- *standard-output* *terminal-io*
- *debug-io* *pop-up-terminal-io*)
- (set-menubar *default-menubar*)
- (%set-toplevel #'toplevel-loop)
- (signal c))
- (1 (handler-bind
- ((serious-condition #'quit-bx))
- (let ((file (make-pathname :name (format nil "~D" (get-universal-time))
- :type "report"
- :directory '(:absolute "binhex-errors")
- :host "home"
- :defaults nil)))
- (with-open-file (s file :direction :output)
- (let ((*error-output* s)
- (*debug-io* s))
- (typecase c
- (condition (report-condition c s))
- (string (princ c s)))
- (print-call-history)))))
- (quit-bx nil))
- (t (quit-bx nil))))
-
-
- (defun binhex-file-error (c)
- (let ((string (report-condition c nil)))
- (ok-cancel-dialog string :ok-text nil)
- (toplevel)))
-
- ; the condition handler for warnings
- (defun binhex-ignore (&rest args)
- (declare (ignore args)))
-
-
- (defun quit-bx (ignore)
- (declare (ignore ignore))
- ; command-. lets one escape from message-dialog
- ; The unwind protect assures that we always quit
- (unwind-protect
- ; it would be cool to quit after 2 minutes
- (message-dialog "Something horrible has happened" :ok-text "Die")
- (quit)))
-
- ;;; The regular toplevel function just hangs out waiting for (menu) events
- (defun binhex-toplevel ()
- (let ((*error-output* *bit-bucket*)
- (*debug-io* *bit-bucket*)
- (*standard-output* *bit-bucket*)
- (*terminal-io* *bit-bucket*)
- (*print-escape* nil)
- (*print-pretty* nil)
- ; below not necessary if we only use ~A
- (*print-readably* nil))
- (handler-bind
- ((file-error #'binhex-file-error)
- (serious-condition #'binhex-unexpected-error)
- (warning #'binhex-ignore))
- ; should this be (event-dispatch t) ?
- (loop (event-dispatch t)))))
-
- ;;; The initial toplevel function installs the regular toplevel function
- ;;; and decodes any finder selected files
- (defun binhex-startup ()
- (let ((*error-output* *bit-bucket*)
- (*debug-io* *bit-bucket*)
- (*print-escape* nil)
- (*print-pretty* nil)
- (*print-readably* nil)))
- (%set-toplevel #'binhex-toplevel)
- (handler-bind
- ((file-error #'binhex-file-error)
- (serious-condition #'binhex-unexpected-error)
- (warning #'binhex-ignore))
- (setq *application* (make-instance 'binhex-application))
- ; process finder selected files if any
- (let ((file-list (finder-parameters)))
- (when (eq (car file-list) :open)
- (dolist (f (cdr file-list))
- (open-application-document *application* f t))))))
-
- ; open and print document handlers
- (defmethod print-application-document ((a binhex-application) file &optional startup)
- (declare (ignore startup file)))
-
- (defmethod open-application-document ((a binhex-application) file &optional startup)
- (declare (ignore startup))
- (let ((type (mac-file-type file)))
- (if (eq type :text)
- (binhex-decode file))))
-
-
- ;;; Get the menubar in the desired state for the standalone application.
- ;;; Then call save-application with the desired toplevel function and creator
-
- (defun load-and-detach (type id)
- (let* ((res (#_get1resource type id)))
- (#_loadresource res)
- (res-error)
- (#_detachresource res)
- (#_HNoPurge res)
- res))
-
-
- (defun save-binhex (path)
- (let* ((apple *apple-menu*)
- (edit (make-instance 'menu :menu-title "Edit"))
- file resources)
- (require "HELP-MANAGER")
- ; because the apple menu is handled specially, if we
- ; try to make a new one, we end up with two.
- (apply 'remove-menu-items apple (menu-items apple))
- ; Put "about binhex" in the apple menu
- (add-menu-items apple
- (make-instance 'menu-item
- :menu-item-title "About Binhex"
- :menu-item-action 'about-binhex)
- (make-instance 'menu-item
- :menu-item-title "-"))
- (remove-menu-items *binhex-menu*
- (find-menu-item *binhex-menu* "Decode Selection…"))
- (setq file (make-instance 'menu
- :menu-title "File"))
- (add-menu-items file
- (make-instance 'menu-item
- :menu-item-title "Quit"
- :menu-item-action #'quit
- :command-key #\Q))
- ; For da's under unifinder. Binhex itself has nothing to edit.
- (let ((undo-item (or (find-menu-item *edit-menu* "Undo")
- (find-menu-item *edit-menu* "Redo")
- ; the darn thing can also be e.g. "Undo Typing"
- (car (slot-value *edit-menu* 'item-list)))))
- (set-menu-item-title undo-item "Undo")
- (add-menu-items edit
- undo-item
- (find-menu-item *edit-menu* "-")
- (find-menu-item *edit-menu* "Cut")
- (find-menu-item *edit-menu* "Copy")
- (find-menu-item *edit-menu* "Paste")
- (find-menu-item *edit-menu* "Clear"))
- (set-menubar (list apple file edit *binhex-menu*)))
- ; The resource file contains icon ("ICN#", "icl8", etc.) resources for the
- ; application (id #128) and for the documents that it creates (id #129)
- ; and appropriate "FREF" and "BNDL" resources.
- ; MCL contains a resource of type "CCL2" and icon and "FREF" resources for
- ; a larger set of document types (ids in the range 128-132).
- (with-open-resource-file (f "ccl:examples;binhex;binhex resources.rsrc")
- (do* ((id 128 (1+ id)))
- ((> id 132))
- (dolist (type '("FREF" "ics#" "ICN#" "icl4" "icl8" "ics4" "ics8"))
- (push (list (if (<= id 129) (load-and-detach type id)) type id) resources)))
- ; We don't want a "CCL2" resource ...
- (push (list nil "CCL2" 0) resources)
- ; We -do- want a resource of type binhex-file-creator ...
- (push (list (#_NewHandle 0) binhex-file-creator 0 "MCL Binhex Example") resources)
- ; Grab "BNDL"(128) from our resource file, replacing MCL's
- (push (list (load-and-detach "BNDL" 128) "BNDL" 128) resources))
- (catch-cancel
- (save-application path :init-file nil :toplevel-function #'binhex-startup
- :creator binhex-file-creator :resources resources :excise-compiler T))
- ; in case the save is cancelled
- (set-menubar *default-menubar*)))
-
- (defun about-binhex ()
- (modal-dialog
- (make-instance 'dialog
- :view-position '(:top 100)
- :view-size #@(180 150)
- :window-type :double-edge-box
- :window-show nil
- :view-subviews
- (list
- (make-dialog-item 'default-button-dialog-item
- #@(55 120) #@(70 18) "OK"
- #'(lambda (item)
- (declare (ignore item))
- (return-from-modal-dialog t)))
- (make-dialog-item 'static-text-dialog-item
- #@(5 5) #@(290 55) (format nil "Binhex in~%~a~%~a"
- (lisp-implementation-type)
- (lisp-implementation-version))
- nil
- :view-font '("geneva" 12 :bold))
- (make-dialog-item 'static-text-dialog-item
- #@(5 60) #@(180 40) "
- Apple Computer, Inc." nil
- :view-font '("geneva" 12))
- ))))
-
-
-
-
-
-
-
-
-
-
-
-